﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;
using System.Runtime.Serialization;
using System.ComponentModel.DataAnnotations;

namespace DarkLight.Model
{
    [DataContract]
    public class Spherical : ObservableObject
    {
        #region Constructors
        public Spherical()
            : this(0,0,0)
        {
        }

        public Spherical(float rho, float theta, float phi)
        {
            this.Rho = rho;
            this.Theta = theta;
            this.Phi = phi;
        }
        #endregion

        #region Static Members
        public static Spherical Zero
        {
            get 
            {   
                return new Spherical(0.0f, 0.0f, 0.0f);
            }
        }

        public static float Distance(Spherical position1, Spherical position2)
        {
            if (object.ReferenceEquals(position1, null))
                throw new ArgumentNullException("position1");
            if (object.ReferenceEquals(position2, null))
                throw new ArgumentNullException("position2");
            else
                return Vector3.Distance(position1.Vector, position2.Vector);
        }
        #endregion

        #region Public Members
        [DataMember]
        public float Rho 
        {
            get { return _Rho; }
            set
            {
                if (_Rho != value)
                {
                    _Rho = value;
                    RaisePropertyChanged("Rho");
                }
            }
        }
        private float _Rho;

        [DataMember]
        [Range(0, 2.0 * Math.PI)]
        public float Theta
        {
            get { return _Theta; }
            set
            {
                if (_Theta != value)
                {
                    _Theta = value;
                    RaisePropertyChanged("Theta");
                }
            }
        }
        private float _Theta;

        [DataMember]
        [Range(0, Math.PI)]
        public float Phi
        {
            get { return _Phi; }
            set
            {
                if (_Phi != value)
                {
                    _Phi = value;
                    RaisePropertyChanged("Phi");
                }
            }
        }
        private float _Phi;

        public Vector3 Vector
        {
            get
            {
                return _Vector;
            }
        }
        private Vector3 _Vector;
        #endregion

        #region ObservableObject Overloads
        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (e.PropertyName == "Rho" || e.PropertyName == "Theta" || e.PropertyName == "Phi")
            {
                this._Vector = this.ToVector3();
            }
            base.OnPropertyChanged(e);
        }
        #endregion

        #region Operators overloads
        public override bool Equals(object obj)
        {
            var sObj = obj as Spherical;
            return this == sObj;
        }

        public static bool operator ==(Spherical sphericalCS1, Spherical sphericalCS2)
        {
            if (object.ReferenceEquals(sphericalCS1, null) && object.ReferenceEquals(sphericalCS2, null))
                return true;
            else if (object.ReferenceEquals(sphericalCS1, null))
                return false;
            else if (object.ReferenceEquals(sphericalCS2, null))
                return false;
            return sphericalCS1.Rho == sphericalCS2.Rho && sphericalCS1.Phi == sphericalCS2.Phi && sphericalCS1.Theta == sphericalCS2.Theta;
        }

        public static bool operator !=(Spherical sphericalCS1, Spherical sphericalCS2)
        {
            if (object.ReferenceEquals(sphericalCS1, null) && object.ReferenceEquals(sphericalCS2, null))
                return false;
            else if (object.ReferenceEquals(sphericalCS1, null))
                return true;
            else if (object.ReferenceEquals(sphericalCS2, null))
                return true;
            return !(sphericalCS1.Rho == sphericalCS2.Rho && sphericalCS1.Phi == sphericalCS2.Phi && sphericalCS1.Theta == sphericalCS2.Theta);
        }

        public static Spherical Add(Spherical sphericalCS1, Spherical sphericalCS2)
        {
            return sphericalCS1 + sphericalCS2;
        }

        public static Spherical operator +(Spherical sphericalCS1, Spherical sphericalCS2)
        {
            if (object.ReferenceEquals(sphericalCS1, null))
                throw new ArgumentNullException("sphericalCS1");
            if (object.ReferenceEquals(sphericalCS2, null))
                throw new ArgumentNullException("sphericalCS2");
            else
                return (sphericalCS1.Vector + sphericalCS2.Vector).ToSphericalCS();
        }

        public static Spherical Subtract(Spherical sphericalCS1, Spherical sphericalCS2)
        {
            return sphericalCS1 - sphericalCS2;
        }

        public static Spherical operator -(Spherical sphericalCS1, Spherical sphericalCS2)
        {
            if (object.ReferenceEquals(sphericalCS1, null))
                throw new ArgumentNullException("sphericalCS1");
            if (object.ReferenceEquals(sphericalCS2, null))
                throw new ArgumentNullException("sphericalCS2");
            else
                return (sphericalCS1.Vector - sphericalCS2.Vector).ToSphericalCS();
        }

        public static Spherical Multiply(Spherical sphericalCS1, float coefficient)
        {
            return sphericalCS1 * coefficient;
        }

        public static Spherical operator *(Spherical sphericalCS, float coefficient)
        {
            if (object.ReferenceEquals(sphericalCS, null))
                throw new ArgumentNullException("sphericalCS");
            return (sphericalCS.Vector * coefficient).ToSphericalCS();
        }

        public static Spherical operator *(float coefficient, Spherical sphericalCS)
        {
            if (object.ReferenceEquals(sphericalCS, null))
                throw new ArgumentNullException("sphericalCS");
            return (coefficient * sphericalCS.Vector).ToSphericalCS();
        }

        public static Spherical Divide(Spherical sphericalCS, float denominator)
        {
            return sphericalCS / denominator;
        }

        public static Spherical operator /(Spherical sphericalCS, float denominator)
        {
            if (object.ReferenceEquals(sphericalCS, null))
                throw new ArgumentNullException("sphericalCS");
            return (sphericalCS.Vector / denominator).ToSphericalCS();
        }

        public override int GetHashCode()
        {
            return (int)(Rho + Theta + Phi);
        }
        #endregion
    }
}
